#ifndef C_TAG_H_INCLUDED
#define C_TAG_H_INCLUDED

#include <string>
#include <vector>
#include <fstream>
#include <cassert>

using namespace std;

class c_tag
{
    private:
        string RAWname;  //The string for the name of the tag for the RAW file
        string XMLname;  //The string for the name of the tag for the XML file
        vector<string> attributes;  //A list of attribute names to use when parsing to XML.  Attributes beyond the names listed are labeled "val"
        vector<string> children;  //A list of tags that can appear as children of this tag, so the program can close it at the appropriate time.
    public:
        /**GET FUNCTIONS**/
        string get_raw_name() {return RAWname;}  //Returns the name of the tag for the RAW file
        string get_xml_name() {return XMLname;}  //Returns the name of the tag for the XML file

        int get_attribute_count() {return attributes.size();}  //Gets the number of attributes
        string get_attribute(int id)
        {
            if( get_attribute_count()>id )
                return attributes.at(id);  //Gets the name of the attribute at "id"
            else
                return attributes.at( attributes.size()-1 );
        }

        int get_child_count() {return children.size();}  //Gets the number of possible children
        string get_child(int id) {return children.at(id);}  //Returns the possible child tag at "id"

        /**SET FUNCTIONS**/
        void set_raw_name(string newname) {RAWname = newname;}  //Sets the RAW name of the tag to the passed argument
        void set_xml_name(string newname) {XMLname = newname;}  //Sets the XML name of the tag to the passed argument
        void set_attribute_name(int id, string newname) {attributes.at(id) = newname;}  //Sets the name of the given attribute to "newname"
        void set_child_name(int id, string newname) {children.at(id) = newname;}  //Sets the name of the given child to "newname"

        /**ADD FUNCTIONS**/
        void add_attribute(string newattr) {attributes.push_back(newattr);}  //Adds an attribute to the list.
        void add_child(string newchild) {children.push_back(newchild);}  //Adds a child to the list.

        /**DEL FUNCTIONS**/
        void del_attribute(int id) { attributes.erase(attributes.begin()+id); }  //Deletes an attribute from the tag
        void del_child(int id) { children.erase(children.begin()+id); }  //Deletes a child from the tag
        void del_all_children() { children.clear(); }  //Deletes all children

        /**OTHER FUNCTIONS**/
        void save(ofstream* file);  //Outputs the tag in binary to a file
        void load(ifstream* file);  //Reads the tag from a file in binary
};

void c_tag::save(ofstream* file)
{
    int rawnamelength = RAWname.size();  //Get the number of characters in the RAW name
    file->write(reinterpret_cast<char*>( &rawnamelength ), sizeof(int));  //Write the length of the RAW name to the file
    for(unsigned int i=0; i<RAWname.size(); i++)
    {
        file->write(reinterpret_cast<char*>( &RAWname.at(i) ), sizeof(char) );  //Add each character in the string to the file
    }

    int xmlnamelength = XMLname.size();  //Get the number of characters in the XML name
    file->write(reinterpret_cast<char*>( &xmlnamelength ), sizeof(int));  //Write the length of the XML name to the file
    for(unsigned int i=0; i<XMLname.size(); i++)
    {
        file->write(reinterpret_cast<char*>( &XMLname.at(i) ), sizeof(char) );  //Add each character in the string to the file
    }

    int attribute_number = attributes.size();  //Get the number of attributes this tag has
    file->write(reinterpret_cast<char*>( &attribute_number ), sizeof(int) );  //Write the number to the file
    for(int i=0; i<attribute_number; i++)  //For each attribute
    {
        int string_len = attributes.at(i).size(); // Get the length of the name of the attribute
        file->write(reinterpret_cast<char*>( &string_len ), sizeof(int) );  //Write the number to the file
        for(int j=0; j<string_len; j++)
        {
            file->write(reinterpret_cast<char*>( &attributes.at(i).at(j) ), sizeof(char) );  //Write each character to the file.
        }
    }

    int child_number = children.size();  //Get the number of children this tag has
    file->write(reinterpret_cast<char*>( &child_number ), sizeof(int) );  //Write the number to the file
    for(int i=0; i<child_number; i++)  //For each child
    {
        int string_len = children.at(i).size(); // Get the length of the name of the child
        file->write(reinterpret_cast<char*>( &string_len ), sizeof(int) );  //Write the number to the file
        for(int j=0; j<string_len; j++)
        {
            file->write(reinterpret_cast<char*>( &children.at(i).at(j) ), sizeof(char) );  //Write each character to the file.
        }
    }
}

void c_tag::load(ifstream* file)
{
    int rawnamelength;  //A variable to read the length of the RAW name into
    file->read( reinterpret_cast<char*>( &rawnamelength ), sizeof(int) );  //Get the length of the RAW name of the tag
    for(int i=0; i<rawnamelength; i++)  //Repeat a number of times equal to the length of the RAW name
    {
        char newchar;  //A character to read into
        file->read( reinterpret_cast<char*>( &newchar ), sizeof(char) );  //Read the character
        RAWname.push_back(newchar);  //Add it to the RAW name
    }

    int xmlnamelength;  //A variable to read the length of the RAW name into
    file->read( reinterpret_cast<char*>( &xmlnamelength ), sizeof(int) );  //Get the length of the RAW name of the tag
    for(int i=0; i<xmlnamelength; i++)  //Repeat a number of times equal to the length of the RAW name
    {
        char newchar;  //A character to read into
        file->read( reinterpret_cast<char*>( &newchar ), sizeof(char) );  //Read the character
        XMLname.push_back(newchar);  //Add it to the XML name
    }

    int attribute_number;  //A variable to read the number of attributes into
    file->read( reinterpret_cast<char*>( &attribute_number ), sizeof(int) );  //Read the number of attributes from the file
    for(int i=0; i<attribute_number; i++) //Repeat a number of times equal to the number of attributes
    {
        string attr_name = "";  //An empty string to read the name of the attribute into
        int attr_name_length;  //A variable to read the length of the attribute's name into
        file->read( reinterpret_cast<char*>( &attr_name_length ), sizeof(int) );  //Read the length of the attribute's name from the file
        for(int j=0; j<attr_name_length; j++)  //Repeat a number of times equal to the length of the name of the attribute
        {
            char newchar;  //A character to read into
            file->read( reinterpret_cast<char*>( &newchar ), sizeof(char) );  //Read the character
            attr_name.push_back(newchar);  //Add it to the name
        }
        attributes.push_back(attr_name);  //Add the attribute to the tag
    }

    int child_number;  //A variable to read the number of children into
    file->read( reinterpret_cast<char*>( &child_number ), sizeof(int) );  //Read the number of children from the file
    for(int i=0; i<child_number; i++) //Repeat a number of times equal to the number of children
    {
        string child_name = "";  //An empty string to read the name of the child into
        int child_name_length;  //A variable to read the length of the child's name into
        file->read( reinterpret_cast<char*>( &child_name_length ), sizeof(int) );  //Read the length of the child's name from the file
        for(int j=0; j<child_name_length; j++)  //Repeat a number of times equal to the length of the name of the child
        {
            char newchar;  //A character to read into
            file->read( reinterpret_cast<char*>( &newchar ), sizeof(char) );  //Read the character
            child_name.push_back(newchar);  //Add it to the name
        }
        children.push_back(child_name);  //Add the child to the tag
    }
}

#endif // C_TAG_H_INCLUDED
